#include "config.h"

#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>

#define DBG_SUBSYS S_LIBCONTROL

#include "limits.h"
#include "adt.h"
#include "sysy_lib.h"
#include "bmap.h"
#include "lich_md.h"
#include "net_table.h"
#include "configure.h"
#include "../replica/replica.h"
#include "table_proto.h"
#include "rmvol_bh.h"
#include "stor_ctl.h"
#include "../storage/stor_rpc.h"
#include "../storage/vnode.h"
#include "pool_proto.h"
#include "pool_rmvol.h"
#include "md_proto.h"
#include "net_global.h"
#include "job_dock.h"
#include "ylog.h"
#include "dbg.h"

typedef pool_proto_entry_t entry_t;


static int __pool_rmvol_set_log(pool_proto_t *pool_proto, const char *_name)
{
        int ret;
        char name[MAX_BUF_LEN];

        base64_encode(_name, strlen(_name) + 1, name);

        ret = pool_proto->xattr_set(pool_proto, LICH_SYSTEM_ATTR_UNLINK,
                                   name, strlen(name) + 1, O_EXCL);
        if (unlikely(ret)) {
                DINFO("pool_rmvol @ "CHKID_FORMAT", ret: %d\n",
                      CHKID_ARG(&pool_proto->chkid), ret);
                YASSERT(ret != EEXIST);
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

static int __pool_rmvol_get_log(pool_proto_t *pool_proto, char *name)
{
        int ret, buflen = MAX_NAME_LEN, len = MAX_NAME_LEN;
        char buf[MAX_BUF_LEN];

        ret = pool_proto->xattr_get(pool_proto, LICH_SYSTEM_ATTR_UNLINK, buf, &buflen);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        base64_decode(buf, &len, name);

        return 0;
err_ret:
        return ret;
}

static int __pool_rmvol_remove_log(pool_proto_t *pool_proto)
{
        return pool_proto->xattr_remove(pool_proto, LICH_SYSTEM_ATTR_UNLINK);
}

static int __pool_rmvol_load(const char *pool)
{
        int ret;
        fileid_t fileid;
        nid_t nid;
        fileinfo_t fileinfo;

        ret = rmvol_bh_root(pool, &fileid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = vnode_location(pool, &fileid, &nid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (net_islocal(&nid)) {
                DBUG("chunk %s %p\n", id2str(&fileid), &fileid);
                ret = stor_ctl_getattr(&fileid, &fileinfo);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        } else {
                ret = stor_rpc_getattr(&nid, &fileid, &fileinfo);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

static int __pool_rmvol__(pool_proto_t *pool_proto, entry_t *ent, int redo)
{
        int ret;
        fileid_t fileid;
        char name[MAX_NAME_LEN];
        const chkinfo_t *chkinfo;

        (void) redo;

        // fileid: /system/unlink
        ret = rmvol_bh_root(pool_proto->pool, &fileid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        chkinfo = ent->chkinfo;
        snprintf(name, MAX_NAME_LEN, CHKID_FORMAT, CHKID_ARG(&chkinfo->id));
        ret = md_mkvolwith(&fileid, name, chkinfo);
        if (unlikely(ret)) {
                if (ret == EEXIST) {
                        YASSERT(redo);
                        DWARN("%s exist\n", name);
                } else
                        GOTO(err_ret, ret);
        }

        ret = chunk_proto_setparent(chkinfo, &fileid);
        if (unlikely(ret)) {
                if (ret == ENOENT) {
                        YASSERT(redo);
                        DWARN("%s not exist\n", name);
                } else
                        GOTO(err_ret, ret);
        }

        //fileinfo set  __FILE_ATTR_DELETE__
        {
                setattr_t setattr;
                memset(&setattr, 0x0, sizeof(setattr));
                setattr.deleting.set_it = 1;
                setattr.deleting.val = 1;
                ret = md_setattr(&chkinfo->id, NULL, &setattr);
                if (unlikely(ret)) {
                        if (ret == EREMCHG) {
                                DERROR("%s not exist\n", name);
                        } else {
                                GOTO(err_ret, ret);
                        }
                }
        }

#if ENABLE_BALANCE
        //must before pool_proto_del, while free this chkinfo ！！！
        ret = stor_rmvol_sync2master(network_rname(&chkinfo->diskid[0].id), &chkinfo->id);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }
#endif
        ret = pool_proto_del(pool_proto, ent->name);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

static int __pool_rmvol(pool_proto_t *pool_proto, const char *name)
{
        int ret;
        entry_t *ent;

        ent = hash_table_find(pool_proto->name_tab, (void *)name);
        if (ent == NULL) {
                ret = ENOENT;
                GOTO(err_ret, ret);
        }

        ret = __pool_rmvol_load(pool_proto->pool);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        // TODO 为何要设置xattr？
        ret = __pool_rmvol_set_log(pool_proto, name);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = __pool_rmvol__(pool_proto, ent, 0);
        if (unlikely(ret))
                GOTO(err_reload, ret);

        ret = __pool_rmvol_remove_log(pool_proto);
        if (unlikely(ret))
                GOTO(err_reload, ret);

        return 0;
err_reload:
        pool_proto->ltime = 0;
err_ret:
        return ret;
}

#if 1
static int __pool_rmvol_force(pool_proto_t *pool_proto, const char *name)
{
        DWARN(" pool "CHKID_FORMAT" rmvol force :%s !\n", CHKID_ARG(&pool_proto->chkid), name);
        return pool_proto_del(pool_proto, name);
}
#endif

/**
 * 强制删除只从pool table中删除记录。
 * 只在极端情况下使用，所以不考虑正常的删除流程。
 * issues:10314
 *
 * @Param pool_proto
 * @Param name
 * @Param force
 *
 * @Returns
 */
int pool_rmvol(pool_proto_t *pool_proto, const char *name, int force)
{
#if 1
        if (force) {
                return __pool_rmvol_force(pool_proto, name);
        } else {
                return __pool_rmvol(pool_proto, name);
        }
#else
        (void) force;
        return __pool_rmvol(pool_proto, name);
#endif
}

int pool_rmvol_load(pool_proto_t *pool_proto)
{
        int ret;
        char name[MAX_NAME_LEN];
        entry_t *ent;

        ret = __pool_rmvol_get_log(pool_proto, name);
        if (unlikely(ret)) {
                if (ret == ENOKEY)
                        goto out;
                else
                        GOTO(err_ret, ret);
        }

        ent = hash_table_find(pool_proto->name_tab, (void *)name);
        if (ent == NULL) {
                ret = __pool_rmvol_remove_log(pool_proto);
                if (ret)
                        GOTO(err_ret, ret);

                goto out;
        }

        ret = __pool_rmvol__(pool_proto, ent, 1);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = __pool_rmvol_remove_log(pool_proto);
        if (unlikely(ret))
                GOTO(err_ret, ret);

out:
        return 0;
err_ret:
        return ret;
}
